/**
 * websocket 类
 * @class
 * @param {object|string} options-传递参数
 * @param {string} options.url-websocket 连接地址
 * @param {string|object[]} [options.protocol]-子协议
 * @param {reconnect} [options.reconnect]-断开后是否自动连接
 * @param {number} [reconnectIntervalInMilliSeconds]-连接时间
 **/
 export function _typeof(value) {
  const class2type = {};
  "Boolean Number String Function Array Date RegExp Object Error".split(" ").forEach((key) => {
    class2type["[object " + key + "]"] = key.toLowerCase();
  });

  if (value === null) return String(value);

  return typeof value === "object" || typeof value === "function" ? class2type[class2type.toString.call(value)] || "object" : typeof value;
}

 const defaultOptions = {
  url: "",
  reconnect: true,
  reconnectIntervalInMilliSeconds: 0,
};

class WebSocketClass {

  constructor(options) {
    let _options = {};
    if (_typeof(options) === 'string') {
      _options = { url: options };
    } else if (_typeof(options) === 'object' && options !== null) {
      _options = options;
    }

    const option = Object.assign({}, defaultOptions, _options);

    this.ws = new WebSocket(option.url);
    this.url = option.url;
    this.reconnect = option.reconnect;
    this.reconnectIntervalInMilliSeconds = option.reconnectIntervalInMilliSeconds;
    this.attempts = 1;
    this.isDestroy = false;

    this.reconnectTimeOut = null;

    this.heart = false;
    this.heartTime = 40000;
    this.heartTimeout = null;
    this.serverHeartTimeout = null;
    this.isReconnected = false; // 是否在重连中

    this.watcher = {
      open: [],
      message: [],
      error: [],
      close: [],
    };

    this._init();
  }

  // Websocket 开启连接时执行
  onOpen(callback) {
    this.watcher['open'].push(callback);
  }

  // websocket 在通信中执行函数
  onMessage(callback) {
    this.watcher['message'].push(callback);
  }

  // websocket 断开连接执行函数
  onClose(callback) {
    this.watcher['close'].push(callback);
  }

  // 销毁当前websocket
  destroy() {
    this.isDestroy = true;

    if (this.ws) this.ws.close();
    this.ws = null;

    // 清除延时
    clearTimeout(this.heartTimeout);
    clearTimeout(this.serverHeartTimeout);
    clearTimeout(this.reconnectTimeOut);
    this.heartTimeout = null;
    this.serverHeartTimeout = null;
    this.reconnectTimeOut = null;
  }

  // 重连
  doReconnect() {
    if (this.reconnect) {
      // 存在ws 则先关闭
      if (this.ws) {
        this.ws.close();
        this.ws = null;
      }

      // 主动destroy，不进行重连
      if (this.isDestroy) return;

      // 重连判断 在重连中则返回
      if (this.isReconnected) return;
      this.isReconnected = true;

      const time = this._generateInterval(this.attempts);
      this.reconnectTimeOut = setTimeout(() => {
        this.attempts += 1;
        this.isReconnected = false;
        this.ws = new WebSocket(this.url);
        this._init();
      }, time);
    }
  }

  _init() {
    this.ws.onopen = () => {
      this.watcher['open'].forEach(fn => fn());
      this.sendHeart();
    };

    this.ws.onmessage = (event) => {
      // 过滤心跳数据
      const data = JSON.parse(event.data) || {};
      if (data.c !== "r" || data.r !== "h") {
        this.watcher['message'].forEach(fn => fn(data));
      }
      this.sendHeart();
    };

    this.ws.onclose = () => {
      this.watcher['close'].forEach(fn => fn());
      this.doReconnect();
    };

    this.ws.onerror = () => { // 默认ws报错就执行重连 todo -- 解决第一次连接失败
      this.doReconnect();
    };
  }

  sendHeart() {
    const that = this;
    clearTimeout(this.heartTimeout);
    clearTimeout(this.serverHeartTimeout);

    if (!this.isDestroy) { // ws 已经删除不做心跳重连
      this.heartTimeout = setTimeout(() => {
        // 发送心跳
        if (this.ws && this.ws.readyState === 1) {
          this.ws.send(JSON.stringify({ 'c': 'h' }));
        }

        that.serverHeartTimeout = setTimeout(() => { // 如果超过一定时间还没重置，说明后端断开了
          that.doReconnect();
        }, that.heartTime);
      }, that.heartTime);
    }
  }

  // websocket 发送消息
  sendMessage(message) {
    const that = this;
    if (message && this.ws) {
      try {
        this.ws.send(JSON.stringify(message));
      } catch (err) {
        const timer = setInterval(() => {
          if (that.ws && that.ws.readyState === 1) {
            that.ws.send(JSON.stringify(message));
            clearInterval(timer);
          }
        }, 200);

        const timer01 = setTimeout(() => {
          clearTimeout(timer01);
          clearInterval(timer);
        }, 10000);
      }
    }
  }

  // 重连时间间隔控制
  _generateInterval(k) {
    if (this.reconnectIntervalInMilliSeconds > 0) {
      return this.reconnectIntervalInMilliSeconds;
    }
    return Math.min(30, (Math.pow(2, k) - 1)) * 1000;
  }
}

const websocket = (options) => new WebSocketClass(options);
export {
  websocket
}